import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:flutter/cupertino.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../config/app_url_address.dart';
import '../utils/currency_manager.dart';
import 'ErrorMessageService.dart';
import 'my_interceptors_wrapper.dart';
import 'package:http_parser/http_parser.dart';


typedef CallbackSuccess = Function(dynamic result);

typedef CallbackFail = Function(String message);

enum RequestMethod { get, post,postJson,postFile }

class DioRequest {
  //工厂方法构造函数
  factory DioRequest() => _getInstance();
  //instance的getter方法，通过DioManager.instance获取对象
  static DioRequest get instance => _getInstance();
  //静态变量_instance,存储唯一对象
  static late DioRequest _instance = DioRequest._internal();
  late Dio dio;
  bool _isDioInit=false;
  //是否开启代理，测试要抓包
  bool  isProxyChecked = false; // a variable for debug

  static DioRequest _getInstance() {
    // ignore: unnecessary_null_comparison, prefer_conditional_assignment
    if (_instance == null) {
      _instance = DioRequest._internal();
    }
    return _instance;
  }

  void refreshBaseUrl(String baseUrl){
    if(_isDioInit){
      dio.options.baseUrl=baseUrl;
    }
  }

  DioRequest._internal() {
    dio = Dio(BaseOptions(
        baseUrl: baseHostUrl,
        connectTimeout: Duration(milliseconds: 20000), //毫秒
        receiveTimeout: Duration(milliseconds: 20000),
        responseType: ResponseType.json,
        headers: {
          'Content-Type': 'application/json;charset=utf-8',
        }));
    _isDioInit=true;
    //添加拦截器 拦截器可以在请求之前、响应之前、error之前做一些预处理
    dio.interceptors.add(MyInterceptor());
    //测试用的
    if(isProxyChecked){
      dio.httpClientAdapter = IOHttpClientAdapter(
        createHttpClient: () {
          final client = HttpClient();
          // Config the client.
          client.findProxy = (uri) {
            // Forward all request to proxy "localhost:8888".
            // Be aware, the proxy should went through you running device,
            // not the host platform.
            return 'PROXY 192.168.1.170:8888';
          };
          client.badCertificateCallback = (cert, host, port) => true;
          // You can also create a new HttpClient for Dio instead of returning,
          // but a client must being returned here.
          return client;
        },
      );
    }
  }
  //get方法
  get(String url,
      {Map<String, dynamic>? parameter,
        bool easyLoading = false, //是否显示加载框
        CallbackSuccess? success,
        CallbackFail? fail,
        needOriginalData=false,
      }) {
    request(url, RequestMethod.get,
        parameter: parameter,
        success: success,
        fail: fail,
        needOriginalData: needOriginalData
    );
  }


  //post方法
  post(String url,
      {Map<String, dynamic>? parameter,
        bool easyLoading = false, //是否显示加载框
        CallbackSuccess? success,
        CallbackFail? fail,
        needOriginalData=false,
      }) {
    request(url, RequestMethod.post,
        parameter: parameter,
        success: success,
        fail: fail,
        needOriginalData: needOriginalData
    );
  }

  Future<String> _getAppInfo() async {
    try {
      PackageInfo packageInfo = await PackageInfo.fromPlatform();
      return packageInfo.version;
    } catch (e) {
      debugPrint("Get app info error: $e");
      return "";
    }
  }
  //needOriginalData 为true代表返回原始数据 不取data的数据了
  //请求公共部分
  request(String url, RequestMethod method,
      {Map<String, dynamic>? parameter,
        String postJson="",
        CallbackSuccess? success,
        CallbackFail? fail,
        needOriginalData=false,
      }) async {
    Response? response;

    debugPrint("url = $url");
    debugPrint("parameter = $parameter");
    String appType = "";
    String appVersion = "";
    
    try {
      appVersion = await _getAppInfo();
    } catch (e) {
      debugPrint("Failed to get app version info: $e");
      appVersion = "1.0.0"; // 默认版本
    }

    if(Platform.isIOS){
      appType = "iOS-Litbuy-Sheets";
    } else {
      appType = "Android-Litbuy-Sheets";
    }
    
    try {
      switch (method) {
        case RequestMethod.get:
          {
            response = await dio.get(url,
                queryParameters: parameter,
                options: Options(
                  headers: _buildHeaders(
                    appVersion: appVersion,
                    appType: appType,
                    // 🔥 GET 请求不设置 contentType，让 Dio 使用默认的 URL 编码（空格 -> %20）
                    // contentType: 'application/x-www-form-urlencoded',
                    useAuthorization: true,
                  ),
                ));
          }
          break;
        case RequestMethod.post:
          {
            String token = "";
            try {
            final prefs = await SharedPreferences.getInstance();
              token = prefs.getString('token') ?? "";
            } catch (e) {
              debugPrint("Failed to get token: $e");
            }

            response = await dio.post(url,
                data: parameter,
                options: Options(
                  headers: _buildHeaders(
                    appVersion: appVersion,
                    appType: appType,
                    token: token,
                    contentType: 'application/json',
                    useAuthorization: false,
                  ),
                ));
          }
          break;
        case RequestMethod.postJson:
          {
            response = await dio.post(url,
                data: postJson,
                options: Options(
                  headers: _buildHeaders(
                    appVersion: appVersion,
                    appType: appType,
                    contentType: 'application/json',
                    useAuthorization: true,
                  ),
                ));
          }
          break;

        case RequestMethod.postFile:
          {
            // 🔥 详细日志：表单提交开始
            debugPrint("=== PostFile 表单提交开始 ===");
            debugPrint("请求URL: $url");
            debugPrint("请求参数: $parameter");
            
            // 构建FormData
            FormData formData = FormData();

            if (parameter != null) {
              debugPrint("=== 构建FormData ===");
              for (var entry in parameter.entries) {
                String key = entry.key;
                dynamic value = entry.value;

                debugPrint("处理字段: $key, 值类型: ${value.runtimeType}");

                // 检查是否为文件路径（支持更多图片格式和其他文件类型）
                if (value is String && _isFilePath(value)) {
                  try {
                    // 检查文件是否存在
                    final file = File(value);
                    if (await file.exists()) {
                      // 获取文件MIME类型
                      String? mimeType = _getMimeType(value);
                      final fileSize = await file.length();
                      final fileName = value.split('/').last;

                      debugPrint("✅ 文件字段: $key");
                      debugPrint("   文件路径: $value");
                      debugPrint("   文件名: $fileName");
                      debugPrint("   文件大小: ${fileSize} bytes (${(fileSize / 1024).toStringAsFixed(2)} KB)");
                      debugPrint("   MIME类型: $mimeType");

                      // 创建MultipartFile
                      formData.files.add(MapEntry(key, await MultipartFile.fromFile(
                        value,
                        filename: fileName,
                        contentType: mimeType != null ? MediaType.parse(mimeType) : null,
                      )));
                    } else {
                      debugPrint('⚠️ 文件不存在: $value');
                      // 如果文件不存在，作为普通字段处理
                      formData.fields.add(MapEntry(key, value.toString()));
                    }
                  } catch (e) {
                    debugPrint('❌ 处理文件出错: $e');
                    // 出错时作为普通字段处理
                    formData.fields.add(MapEntry(key, value.toString()));
                  }
                } else if (value is File) {
                  // 直接传入File对象的情况
                  try {
                    if (await value.exists()) {
                      String? mimeType = _getMimeType(value.path);
                      final fileSize = await value.length();
                      final fileName = value.path.split('/').last;

                      debugPrint("✅ File对象字段: $key");
                      debugPrint("   文件路径: ${value.path}");
                      debugPrint("   文件名: $fileName");
                      debugPrint("   文件大小: ${fileSize} bytes (${(fileSize / 1024).toStringAsFixed(2)} KB)");
                      debugPrint("   MIME类型: $mimeType");

                      formData.files.add(MapEntry(key, await MultipartFile.fromFile(
                        value.path,
                        filename: fileName,
                        contentType: mimeType != null ? MediaType.parse(mimeType) : null,
                      )));
                    } else {
                      debugPrint('⚠️ File对象不存在: ${value.path}');
                    }
                  } catch (e) {
                    debugPrint('❌ 处理File对象出错: $e');
                  }
                } else {
                  // 普通字段
                  debugPrint("📝 普通字段: $key = $value");
                  formData.fields.add(MapEntry(key, value.toString()));
                }
              }
            }

            // 🔥 详细日志：FormData构建完成
            debugPrint("=== FormData构建完成 ===");
            debugPrint("文件数量: ${formData.files.length}");
            debugPrint("普通字段数量: ${formData.fields.length}");
            for (var file in formData.files) {
              debugPrint("文件字段: ${file.key}");
            }
            for (var field in formData.fields) {
              debugPrint("普通字段: ${field.key} = ${field.value}");
            }

            // 获取token
            String token = "";
            try {
              final prefs = await SharedPreferences.getInstance();
              token = prefs.getString('token') ?? "";
              debugPrint("Token: ${token.isEmpty ? '无' : '已获取'}");
            } catch (e) {
              debugPrint("❌ 获取token失败: $e");
            }

            debugPrint("=== 开始发送表单请求 ===");
            response = await dio.post(url,
                data: formData,
                options: Options(
                  headers: _buildHeaders(
                    appVersion: appVersion,
                    appType: appType,
                    token: token,
                    contentType: 'multipart/form-data',
                    useAuthorization: false,
                  ),
                ));
            debugPrint("✅ 表单请求发送成功");
          }
          break;
      }

      debugPrint("Request response: $response");

      if (response.statusCode == 200) {
        var result = jsonDecode(response.toString());
        if (result['success'] == true) {
          if(needOriginalData){
            success?.call(result);
          }else{
            debugPrint("Request successful: ${result['result']}");
            success?.call(result['result']);
          }
          return;
        } else {
          String errorMessage = result['message'] ?? 'Unknown error';
          if (result['code'] == 502 || result['code'] == 401) {
            // 处理认证错误
          } else if (result['code'] == 500) {
            // 处理服务器错误
          } else if (result['code'] == 5001 || result['code'] == 5002) {
            // 处理业务错误
          }
          fail?.call(errorMessage);
        }
      } else {
        String errorMessage = response.statusMessage ?? "Unknown error";
        fail?.call(errorMessage);
      }

    } on DioException catch (error) {
      String errorType = handleErrorInfo(error);
      if(errorType.isEmpty){
        errorType = ErrorMessageService.getNetworkUnavailable();
      }
      String message = error.response?.toString() ?? errorType;
      fail?.call(message);
    } catch (e) {
      debugPrint("Unknown network request error: $e");
      fail?.call("Network request failed: ${e.toString()}");
    }
  }

  //错误统一处理
  String handleErrorInfo(DioException error) {
    String errorMessage = '';
    switch (error.type) {
      case DioExceptionType.connectionTimeout:
        errorMessage = ErrorMessageService.getNetworkError();
        break;
      case DioExceptionType.sendTimeout:
        errorMessage = ErrorMessageService.getNetworkError();
        break;
      case DioExceptionType.receiveTimeout:
        errorMessage = ErrorMessageService.getNetworkError();
        break;
      case DioExceptionType.badResponse:
        errorMessage = ErrorMessageService.getNetworkError();
        break;
      case DioExceptionType.badCertificate:
        errorMessage = ErrorMessageService.getNetworkError();
        break;
      case DioExceptionType.connectionError:
        errorMessage = ErrorMessageService.getNetworkUnavailable();
        break;
      case DioExceptionType.cancel:
        errorMessage = ErrorMessageService.getCancel();
        break;
      case DioExceptionType.unknown:
        errorMessage = ErrorMessageService.getNetworkError();
        break;
      default:
    }
    return errorMessage;
    // MyToast.toast(errorMessage);

  }

  /// 构建请求头，只在token有值时添加token字段
  Map<String, String> _buildHeaders({
    required String appVersion,
    required String appType,
    String? token,
    String? contentType,
    bool useAuthorization = false,
  }) {


    Map<String, String> headers = {
      'APPVersion': appVersion,
      'APPName': appType,
      'currency': CurrencyManager.instance.currentCurrencyCode,

    };

    // 只在token有值时添加token字段
    if (token != null && token.trim().isNotEmpty) {
      if (useAuthorization) {
        headers['Authorization'] = token;
      } else {
        headers['X-Access-Token'] = token;
      }
    }

    // 添加Content-Type（如果指定）
    if (contentType != null) {
      headers['Content-Type'] = contentType;
    }

    return headers;
  }

  //get方法
  getBloc(String url,
      {Map<String, dynamic>? parameter,
        bool easyLoading = false, //是否显示加载框
        CallbackSuccess? success,
        CallbackFail? fail,
        needOriginalData=false,
      }) async {
    await request(url, RequestMethod.get,
        parameter: parameter,
        success: success,
        fail: fail,
        needOriginalData: needOriginalData
    );
  }
  //post方法
  postBloc(String url,
      {Map<String, dynamic>? parameter,
        bool easyLoading = false, //是否显示加载框
        CallbackSuccess? success,
        CallbackFail? fail,
        needOriginalData=false,
      }) async {
    await request(url, RequestMethod.post,
        parameter: parameter,
        success: success,
        fail: fail,
        needOriginalData: needOriginalData
    );


  }

  //post方法
  postBlocJson(String url,String postJson,
      {

        bool easyLoading = false, //是否显示加载框
        CallbackSuccess? success,
        CallbackFail? fail,
        needOriginalData=false,
      }) async {
    await request(url, RequestMethod.postJson,
        postJson:postJson ,
        success: success,
        fail: fail,
        needOriginalData: needOriginalData
    );
  }

  PostFile(String url,
      {Map<String, dynamic>? parameter,
        bool easyLoading = false, //是否显示加载框
        CallbackSuccess? success,
        CallbackFail? fail,
        needOriginalData=false,
      }) async {
    await request(url, RequestMethod.postFile,
        parameter: parameter,
        success: success,
        fail: fail,
        needOriginalData: needOriginalData
    );
  }
  // 辅助方法：判断是否为文件路径
  bool _isFilePath(String path) {
    // 检查常见的文件扩展名
    final fileExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.pdf', '.doc', '.docx', '.txt', '.mp4', '.avi', '.mov'];
    final lowerPath = path.toLowerCase();
    return fileExtensions.any((ext) => lowerPath.endsWith(ext)) || path.contains('/');
  }

  // 辅助方法：获取文件MIME类型
  String? _getMimeType(String filePath) {
    final extension = filePath.toLowerCase().split('.').last;
    switch (extension) {
      case 'jpg':
      case 'jpeg':
        return 'image/jpeg';
      case 'png':
        return 'image/png';
      case 'gif':
        return 'image/gif';
      case 'bmp':
        return 'image/bmp';
      case 'webp':
        return 'image/webp';
      case 'pdf':
        return 'application/pdf';
      case 'doc':
        return 'application/msword';
      case 'docx':
        return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
      case 'txt':
        return 'text/plain';
      case 'mp4':
        return 'video/mp4';
      case 'avi':
        return 'video/x-msvideo';
      case 'mov':
        return 'video/quicktime';
      default:
        return null;
    }
  }
}

